
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>事务消息与分布式事务 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>事务消息与分布式事务 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">概念</div>
          <div class="card-question">RocketMQ事务消息的核心定义是什么？它主要解决了什么问题？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">概念</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">RocketMQ事务消息是指将消息发送和本地事务执行捆绑在一起的一种机制，旨在保证本地事务和消息发送这两个操作要么都成功，要么都失败。它主要用于解决分布式系统中的数据一致性问题。</div>
          </div>
          <div class="card-source">来源: 事务消息的概念</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">什么是“半消息”（Half Message），它在RocketMQ事务流程中扮演什么角色？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">半消息是Producer发送给Broker的第一阶段消息，它对Consumer是不可见的。其核心作用是预占消息资源，并将消息发送与本地事务执行解耦，确保在本地事务提交成功之前，下游消费者不会收到该消息。</div>
          </div>
          <div class="card-source">来源: 1. 事务消息发送流程 & 1. 半消息机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">流程</div>
          <div class="card-question">请简述RocketMQ事务消息的三个主要发送步骤。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">流程</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">1. **发送半消息**：Producer向Broker发送一条对Consumer不可见的“半消息”。<br>2. **执行本地事务**：Producer在收到半消息发送成功的响应后，开始执行本地业务事务。<br>3. **提交或回滚事务**：根据本地事务的执行结果（成功、失败或未知），Producer向Broker发送确认请求，以提交或回滚之前的半消息。</div>
          </div>
          <div class="card-source">来源: 1. 事务消息发送流程</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">RocketMQ的事务状态回查机制在什么情况下会被触发？它的主要目的是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">当Producer未能及时发送事务的最终确认消息（提交或回滚），例如由于网络问题或生产者应用崩溃，Broker会定期向该Producer发起事务状态回查。其主要目的是为了解决事务状态不确定的问题，确保事务消息的最终一致性，避免消息长时间处于中间状态。</div>
          </div>
          <div class="card-source">来源: 2. 事务状态回查机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">如果事务状态回查达到最大次数后仍未得到明确结果，RocketMQ会如何处理这条消息？默认的回查次数是多少？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">如果超过了配置的最大回查次数，消息默认会被回滚。文档中提到的默认回查次数是15次。</div>
          </div>
          <div class="card-source">来源: 2. 事务状态回查机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实现</div>
          <div class="card-question">在RocketMQ的存储结构中，未被确认的半消息存放在哪里？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实现</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">未被确认的半消息存储在一个特殊的系统主题中，其名称为：RMQ_SYS_TRANS_HALF_TOPIC。</div>
          </div>
          <div class="card-source">来源: 3. 事务消息的存储结构</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实践</div>
          <div class="card-question">为什么在实现事务消息时，事务回查接口（checkLocalTransaction）必须保证幂等性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实践</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为在网络不稳定或Broker重启等异常情况下，回查请求可能会被多次发送。如果接口不具备幂等性，重复的调用可能会导致错误的业务逻辑或数据不一致。因此，必须确保回查接口无论被调用多少次，对于同一个事务的结果都是一致的。</div>
          </div>
          <div class="card-source">来源: 3. 幂等性设计 & 3. 注意事项</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">应用</div>
          <div class="card-question">在电商订单处理场景中，如何利用事务消息来保证数据一致性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">应用</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以通过以下步骤保证一致性：首先发送一个关于“订单创建”的半消息；然后执行本地事务，即在数据库中创建订单记录；最后，如果本地事务成功，则提交半消息，触发后续的库存扣减和支付单生成等操作。如果本地事务失败，则回滚半消息。</div>
          </div>
          <div class="card-source">来源: 1. 电商订单处理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实践</div>
          <div class="card-question">在实现`TransactionListener`的`executeLocalTransaction`方法时，除了提交和回滚，还有哪种状态？它代表什么含义？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实践</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">除了`COMMIT_MESSAGE`（提交）和`ROLLBACK_MESSAGE`（回滚），还有`UNKNOW`（未知）状态。这个状态表示本地事务执行过程中发生了异常或超时，导致其最终状态不确定，此时Broker会等待并触发后续的事务状态回查来确定最终结果。</div>
          </div>
          <div class="card-source">来源: 1. 实现可靠的事务状态回查 (代码示例)</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
